/*
 ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1

 * The contents of this file are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Blazing Games Inc. (Billy D. Spelchan) code.
 *
 * The Initial Developer of the Original Code is
 * Blazing Games Inc.
 * Portions created by the Initial Developer are Copyright (C) 1999, 2003, 2005-2006
 * the Initial Developer. All Rights Reserved.
 * 
 * Contributor(s):
 *
 ***** END LICENSE BLOCK *****
 */
package com.blazinggames.holeman;

//import java.applet.AudioClip;

public class Monster
extends Sprite {
// *** CONSTANTS ***
  // Internal movement modes
  private final static int MOVE_NONE = 0;
  private final static int MOVE_LEFT = 1;
  private final static int MOVE_RIGHT = 2;
  private final static int MOVE_CHAINLEFT = 3;
  private final static int MOVE_CHAINRIGHT = 4;
  private final static int MOVE_UPLADDER = 5;
  private final static int MOVE_DOWNLADDER = 6;
  private final static int MOVE_UPROPE = 7;
  private final static int MOVE_DOWNROPE = 8;
  private final static int MOVE_FALL = 9;
  private final static int MOVE_DIE = 10;

  /*
    Internal animation sequence variables
    first index is the movement mode (see above),
    second index is the frame (animation lengths may vary),
    third index is the animation element.  It has three components
      [?][?][0] is the actual image tile to show
      [?][?][1] is the offset in the horizontal direction
      [?][?][2] is the offset in the vertical direction
  */
  private final static int ANIMATION[][][] = {
    // MOVE_NONE
    { {FRM_MONSTER, 0, 0} },
    // MOVE_LEFT
    { {FRM_MONSTER_WALKL1, 0, 0}, {FRM_MONSTER_WALKL2, 0, 0}, {FRM_MONSTER_WALKL0, -8, 0},
      {FRM_MONSTER_WALKL1, 0, 0}, {FRM_MONSTER_WALKL2, 0, 0}, {FRM_MONSTER_WALKL0, -8, 0},
      {FRM_MONSTER_WALKL1, 0, 0}, {FRM_MONSTER_WALKL2, 0, 0}, {FRM_MONSTER_WALKL0, -8, 0},
      {FRM_MONSTER_WALKL1, 0, 0}, {FRM_MONSTER_WALKL2, 0, 0}, {FRM_MONSTER_WALKL0, -8, 0} },
    // MOVE_RIGHT
    { {FRM_MONSTER_WALKR1, 0, 0}, {FRM_MONSTER_WALKR2, 0, 0}, {FRM_MONSTER_WALKR0, 8, 0},
      {FRM_MONSTER_WALKR1, 0, 0}, {FRM_MONSTER_WALKR2, 0, 0}, {FRM_MONSTER_WALKR0, 8, 0},
      {FRM_MONSTER_WALKR1, 0, 0}, {FRM_MONSTER_WALKR2, 0, 0}, {FRM_MONSTER_WALKR0, 8, 0},
      {FRM_MONSTER_WALKR1, 0, 0}, {FRM_MONSTER_WALKR2, 0, 0}, {FRM_MONSTER_WALKR0, 8, 0} },
    // MOVE_CHAINLEFT
    { {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN2, 0, 0}, {FRM_MONSTER_CHAIN0, -8, 0},
      {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN2, 0, 0}, {FRM_MONSTER_CHAIN0, -8, 0},
      {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN2, 0, 0}, {FRM_MONSTER_CHAIN0, -8, 0},
      {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN2, 0, 0}, {FRM_MONSTER_CHAIN0, -8, 0} },
    // MOVE_CHAINRIGHT
    { {FRM_MONSTER_CHAIN2, 8, 0}, {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN0, 0, 0},
      {FRM_MONSTER_CHAIN2, 8, 0}, {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN0, 0, 0},
      {FRM_MONSTER_CHAIN2, 8, 0}, {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN0, 0, 0},
      {FRM_MONSTER_CHAIN2, 8, 0}, {FRM_MONSTER_CHAIN1, 0, 0}, {FRM_MONSTER_CHAIN0, 0, 0} },
    //  MOVE_UPLADDER = 7;
    { {FRM_MONSTER_LADDER2, 0, -4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, -4},
      {FRM_MONSTER_LADDER2, 0, -4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, -4},
      {FRM_MONSTER_LADDER2, 0, -4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, -4},
      {FRM_MONSTER_LADDER2, 0, -4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, -4} },
    //  MOVE_DOWNLADDER = 8;
    { {FRM_MONSTER_LADDER2, 0, 4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, 4},
      {FRM_MONSTER_LADDER2, 0, 4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, 4},
      {FRM_MONSTER_LADDER2, 0, 4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, 4},
      {FRM_MONSTER_LADDER2, 0, 4}, {FRM_MONSTER_LADDER0, 0, 0}, {FRM_MONSTER_LADDER1, 0, 4} },
    //  MOVE_UPROPE = 9;
    { {FRM_MONSTER_ROPE0, 0, -2}, {FRM_MONSTER_ROPE1, 0, -2},
      {FRM_MONSTER_ROPE0, 0, -3}, {FRM_MONSTER_ROPE1, 0, -3},
      {FRM_MONSTER_ROPE0, 0, -3}, {FRM_MONSTER_ROPE1, 0, -3},
      {FRM_MONSTER_ROPE0, 0, -2}, {FRM_MONSTER_ROPE1, 0, -2},
      {FRM_MONSTER_ROPE0, 0, -3}, {FRM_MONSTER_ROPE1, 0, -3},
      {FRM_MONSTER_ROPE0, 0, -3}, {FRM_MONSTER_ROPE1, 0, -3} },
    //  MOVE_DOWNROPE = 10;
    { {FRM_MONSTER_ROPE0, 0, 4}, {FRM_MONSTER_ROPE1, 0, 4},
      {FRM_MONSTER_ROPE0, 0, 4}, {FRM_MONSTER_ROPE1, 0, 4},
      {FRM_MONSTER_ROPE0, 0, 4}, {FRM_MONSTER_ROPE1, 0, 4},
      {FRM_MONSTER_ROPE0, 0, 4}, {FRM_MONSTER_ROPE1, 0, 4} },
    //  MOVE_FALL = 11;
    { {FRM_MONSTER_FALL0, 0, 4}, {FRM_MONSTER_FALL1, 0, 4},
      {FRM_MONSTER_FALL0, 0, 4}, {FRM_MONSTER_FALL1, 0, 4},
      {FRM_MONSTER_FALL0, 0, 4}, {FRM_MONSTER_FALL1, 0, 4},
      {FRM_MONSTER_FALL0, 0, 4}, {FRM_MONSTER_FALL1, 0, 4} },
    // MOVE_DIE
    { {FRM_MONSTER_DEATH, 0, 1}, {FRM_MONSTER_DEATH+1, 0, 1},
      {FRM_MONSTER_DEATH+2, 0, 1}, {FRM_MONSTER_DEATH+3, 0, 1},
      {FRM_MONSTER_DEATH+4, 0, 1}, {FRM_MONSTER_DEATH+5, 0, 1},
      {FRM_MONSTER_DEATH+6, 0, 1}, {FRM_MONSTER_DEATH+7, 0, 1} }
  };

// *** VARIABLES ***
  int nMoveMode;

  int nDelay;

  int nCurFrame, nCurDelay;

  boolean bMoving;
  boolean bMonsterDead, bDeathShown;

  int nTargetX, nTargetY;
  int nLastTargetX, nLastTargetY;

// *** CONSTRUCTORS ***
  public Monster(int x, int y) {
    super(x, y, true);

    nTargetX = x;
    nTargetY = y;
    nMoveMode = 0;
    bMoving = false;
    bMonsterDead = bDeathShown = false;
    nDelay = 1;

  }


// *** METHODS - GET ***
  public int getImageID() {
    if (nImageID == (FRM_MONSTER_DEATH + 7) )
      bDeathShown = true;
    return nImageID;
  }

// *** METHODS - SET ***
// *** METHODS - ACTIONS ***
  public void moveOffset(int nOffX, int nOffY) {
    nX += nOffX;
    nY += nOffY;
  }

  private boolean checkTarget(Map map, int x, int y) {
    Monster m;

    for (int cntr = 0; cntr < map.vMonsters.size(); ++cntr) {
      m = (Monster)(map.vMonsters.elementAt(cntr));
      if ( (m.nTargetX == x) && (m.nTargetY == y) )
        return false;
    }

    nLastTargetX = x;
    nLastTargetY = y;
    return true;
  }

  /*
    is falling.
  */
  private void checkFall(Map map) {
    int nMX = nX / 32;
    int nMY = nY / 32;

    if (!checkTarget(map, nMX, nMY+1))
      return;

    if (map.getTile(nMX, nMY+1) == Map.TILE_HOLE) {
      nMoveMode = MOVE_DIE;

      Hole h;
      for (int cntr = 0; cntr < map.vHoles.size(); ++cntr) {
        h = (Hole)(map.vHoles.elementAt(cntr));
        if ( (h.isVisible()) && (h.getMapX() == nMX) && (h.getMapY() == (nMY+1)) ) {
          h.destroyHole(map);
          if (map.bSoundEnabled)
            map.auMonster.play();
        }
      }

      bMoving = true;
      return;
    }

    int nFallTile = map.getTile(nMX, nMY+1);

    if ( (nFallTile < 5 ) || (nFallTile == Map.TILE_CHAIN) ){
      int nTile = map.getTile(nMX, nMY);
      if ( (nTile  < 8) || (nTile > 10) ) {
        nMoveMode = MOVE_FALL;
        nLastTargetX = nTargetX = nMX;
        nLastTargetY = nTargetY = nMY+1;
        bMoving = true;
      }

    }
  }

  private void doMove(Map map) {
    ++nCurDelay;

    if (bMonsterDead) {
      nImageID = (FRM_MONSTER_DEATH + 7);
      return;
    }

    if (nCurDelay >= map.getDelay()) {
      nCurDelay = 0;
        nImageID = ANIMATION[nMoveMode][nCurFrame][0];
        nX += ANIMATION[nMoveMode][nCurFrame][1];
        nY += ANIMATION[nMoveMode][nCurFrame][2];
      ++nCurFrame;
      if (nCurFrame >= ANIMATION[nMoveMode].length) {
        if (nMoveMode == MOVE_DIE)
          bMonsterDead = true;
        nCurFrame = 0;
        bMoving = false;
        checkFall(map);
      }
    }
  }

  private void findNearestClimbUp(Map map) {
    int nMX = nX / 32;
    int nMY = nY / 32;
    int nUpTile, nTile = map.getTile(nMX, nMY-1);
    boolean bCanMoveUp = true;
    if (((nTile > Map.TILE_HOLE) && (nTile < Map.TILE_LADDER)) ||
          (!checkTarget(map, nMX, nMY-1)))
      bCanMoveUp = false;
    nTile = map.getTile(nMX, nMY);
    if ((bCanMoveUp) && (nTile == Map.TILE_LADDER))  {
      nMoveMode = MOVE_UPLADDER;
      bMoving = true;
    }
    else if ((bCanMoveUp) && (nTile == Map.TILE_ROPE)) {
      nMoveMode = MOVE_UPROPE;
      bMoving = true;
    }
    else {
      int cntr, nLeft = 20, nRight = 20;
      for (cntr = nMX; cntr >= 0; --cntr) {
        nTile = map.getTile(cntr, nMY);
        nUpTile = map.getTile(cntr, nMY-1);
        if  ( ((nTile == Map.TILE_LADDER) || (nTile == Map.TILE_ROPE)) &&
              ((nUpTile <= Map.TILE_LAST_FALLTHROUGH)||(nUpTile >= Map.TILE_FIRST_CLIMBABLE)) ) {
          nLeft = nMX - cntr;
          break;
        }
        else if ((nTile >= Map.TILE_BRICK) && (nTile <= Map.TILE_STEEL) )
          break;
      }
      for (cntr = nMX; cntr < Map.MAPWIDTH; ++cntr) {
        nTile = map.getTile(cntr, nMY);
        nUpTile = map.getTile(cntr, nMY-1);
        if ( ((nTile == Map.TILE_LADDER) || (nTile == Map.TILE_ROPE)) &&
              ((nUpTile <= 4)||(nUpTile >= 8)) ) {
          nRight = cntr - nMX;
          break;
        }
        else if ((nTile >= Map.TILE_BRICK) && (nTile <= Map.TILE_STEEL) )
          break;
      }
      if (nRight < nLeft)
        moveRight(map);
      else
        moveLeft(map);
    }
  }

  private void findNearestClimbDown(Map map) {
    int nTile = map.getTile(getMapX(), getMapY()+1);
    if (!checkTarget(map, getMapX(), getMapY()+1))
      nTile = Map.TILE_BRICK;
    if (nTile == Map.TILE_LADDER)  {
      nMoveMode = MOVE_DOWNLADDER;
      bMoving = true;
    }
    else if (nTile == Map.TILE_ROPE) {
      nMoveMode = MOVE_DOWNROPE;
      bMoving = true;
    }
    else if ((nTile == Map.TILE_EMPTY) || (nTile == Map.TILE_CHAIN) ) {
      nMoveMode = MOVE_FALL;
      bMoving = true;
    }
    else {
      int cntr, nLeft = 20, nRight = 20;
      int nMX = nX / 32;
      int nMY = nY / 32;
      int nTilePath;
      for (cntr = nMX; cntr >= 0; --cntr) {
        nTile = map.getTile(cntr, nMY+1);
        nTilePath = map.getTile(cntr, nMY);
        if ((nTile == Map.TILE_LADDER) || (nTile == Map.TILE_ROPE) || (nTile < 4)) {
          nLeft = nMX - cntr;
          break;
        }
        else if ((nTilePath >= Map.TILE_BRICK) && (nTilePath <= Map.TILE_STEEL) )
          break;
      }
      for (cntr = nMX; cntr < Map.MAPWIDTH; ++cntr) {
        nTile = map.getTile(cntr, nMY+1);
        nTilePath = map.getTile(cntr, nMY);
        if ((nTile == Map.TILE_LADDER) || (nTile == Map.TILE_ROPE) || (nTile < 4)) {
          nRight = cntr - nMX;
          break;
        }
        else if ((nTilePath >= Map.TILE_BRICK) && (nTilePath <= Map.TILE_STEEL) )
          break;
      }
      if (nRight < nLeft)
        moveRight(map);
      else
        moveLeft(map);
    }
  }

  private void moveLeft(Map map) {

    int nTile = map.getTile(getMapX()-1, getMapY());
    if (!checkTarget(map,getMapX()-1, getMapY()))
      nTile = Map.TILE_BRICK;
    if ((nTile > 3) && (nTile < 8))
      nMoveMode = MOVE_NONE;
    else if (nTile == Map.TILE_CHAIN)
      nMoveMode = MOVE_CHAINLEFT;
    else
      nMoveMode = MOVE_LEFT;
    bMoving = true;
  }

  private void moveRight(Map map) {
    int nTile = map.getTile(getMapX()+1, getMapY());
    if (!checkTarget(map,getMapX()+1, getMapY()))
      nTile = Map.TILE_BRICK;
    if ((nTile > 3) && (nTile < 8))
      nMoveMode = MOVE_NONE;
    else if (nTile == Map.TILE_CHAIN)
      nMoveMode = MOVE_CHAINRIGHT;
    else
      nMoveMode = MOVE_RIGHT;
    bMoving = true;
  }

  private void decideMove(Map map) {
    if (bMonsterDead)
      return;
    int nMX = nX / 32;
    int nMY = nY / 32;
    int nPX = map.player.getMapX();
    int nPY = map.player.getMapY();

    nLastTargetX = nMX;
    nLastTargetY = nMY;

//    int nTile = map.getTile(nMX, nMY);

    if (nPY < nMY) // Have to move up
      findNearestClimbUp(map);
    else if (nPY > nMY) // Have to move down
      findNearestClimbDown(map);
    else if (nPX < nMX) // Have to move left
      moveLeft(map);
    else if (nPX > nMX) // Have to move Right
      moveRight(map);

      if ((nLastTargetX != nTargetX) || (nLastTargetY != nTargetY)) {
        if (nMoveMode != MOVE_NONE) {
          nTargetX = nLastTargetX;
          nTargetY = nLastTargetY;
        }
        else {
          nLastTargetX = nTargetX;
          nLastTargetY = nTargetY;
        }
      }

  }

  public boolean doUpdate(Map map) {
    if (bMoving)
      doMove(map);
    else
      decideMove(map);

    if ((bMonsterDead) && (bDeathShown) )
      return false;
    return true;
  }

}
